use std::fmt;
use std::io::{Result,ErrorKind,Error};

use super::umutex::{Umutex,ScopedLock};
use super::mbox::MBox;
use super::block::{NilBlock,get_mem};
use super::cpblock::CPBlock;
use super::gp::{GTP,GP};
use super::raw::Raw;
use super::ubox::UBox;
use super::layout;
use mem_macro::MemAlloc;

#[repr(C)]
#[derive(Debug,Default)]
pub struct Bar {
    pub pos :usize,
    pub bar_mutex :Umutex,
}

impl fmt::Display for Bar { 
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,"bar:{}",self.pos)
    }
}

impl Bar {
    pub fn inc(&mut self,s :usize) -> usize {
        let _l = ScopedLock::new(&mut self.bar_mutex);
        let r = self.pos;
        self.pos += s;
        r
    }
}

pub trait BarInc {
    fn inc(&mut self,s :usize) -> usize;
    fn pos(&self) -> usize;
}

impl Bar {
    pub fn new_gtp<P:Sized>(&mut self,b:&mut CPBlock<NilBlock>) -> Result<GTP<P>> {
        match &b.data {
            None => Err(Error::new(ErrorKind::Other,"donot have mem")),
            Some(mr) => {
                let s = std::mem::size_of::<P>();
                let o = self.inc(s);
                Ok(GTP::<P>::from_gp(&GP{id:mr.id,offset:o as u64, size:s as u64})?) 
            }
        }
    }
    pub fn new_gpr<P:Sized>(&mut self,b:&mut CPBlock<NilBlock>) -> Result<(GTP<P>,Raw)> {
        let gtp = self.new_gtp::<P>(b)?;
        let offset = gtp.gp.offset.clone();
        Ok((gtp,get_mem(b,offset as usize)))
    }
    pub fn new<P:Sized>(&mut self,b:&mut CPBlock<NilBlock>) -> Result<UBox<P>> {
        let (gtp,raw) = self.new_gpr::<P>(b)?;
        Ok(UBox::<P>::from_gpr(gtp,raw)?)
    }
    pub fn new_gp(&mut self,b:&mut CPBlock<NilBlock>,s :usize) -> Result<GP> {
        match &b.data {
            None => Err(Error::new(ErrorKind::Other,"donot have mem")),
            Some(mr) => {
                let o = self.inc(s);
                Ok(GP{id:mr.id,offset:o as u64, size:s as u64})
            } 
        }
    }
}

pub trait MemAlloc {
    fn new_gtp<P:Sized>(&mut self) -> Result<GTP<P>>;
    fn new_gpr<P:Sized>(&mut self) -> Result<(GTP<P>,Raw)>;
    fn new<P:Sized>(&mut self) -> Result<UBox<P>>;
    fn new_gp(&mut self,s :usize) -> Result<GP>;
    fn to_raw(&self,s :usize) -> Raw;
}

struct mem_addon {
    bar: MBox<Bar>
}
#[derive(MemAlloc)]
pub struct Mem {
    pub block: CPBlock<NilBlock>,
    addon: mem_addon
}

impl Mem {
    pub fn attach(fname: String) -> Result<Self> {
        let cpb = CPBlock::<NilBlock>::attach(fname)?;
        let aon = cpb.head.as_subtype::<Bar>(layout::ADDONPOST)?;
        Ok(Mem{block: cpb, addon:mem_addon{bar:aon}})
    }
}

